const fs = require('fs');
const path = require('path');
// 转换ast语法树
const parser = require('@babel/parser');
// 节点遍历
const traverse = require('@babel/traverse').default;
// ES6->ES5
const babel = require('@babel/core');
const { tsNonNullExpression } = require('@babel/types');


// 分析单个文件的文件依赖
function getModuleInfo(file) {
  // 读取文件
  const body = fs.readFileSync(file, 'utf-8');
  // 转换ast语法树
  const ast = parser.parse(body, {
    sourceType: "module"
  })
  // 依赖收集，import xxx
  const deps = {};
  // 节点遍历->将ast语法树中所有节点遍历一遍
  traverse(ast, {
    // visitor（访问器）
    // 依赖收集——转换路径
    ImportDeclaration({ node }) {
      const dirname = path.dirname(file);
      const abspath = "./" + path.join(dirname, node.source.value);
      deps[node.source.value] = abspath;
    }
  });
  // ES6转ES5
  const { code } = babel.transformFromAst(ast, null, {
    //预设
    presets:["@babel/preset-env"],
  });
  const info = {
    file,
    deps,
    code,
  };
  return info;
};
// 相对于webpack的路径
// const info = getModuleInfo("./src/index.js");
// console.log(info);

// 模块解析---循环依赖收集
function parseModules(file) {
  const entry = getModuleInfo(file);
  const temp = [entry]
  const depsGraph = {}

  getDeps(temp,entry);
  temp.forEach(info=>{
    depsGraph[info.file] = {
      deps:info.deps,
      code: info.code,
    }
  })
  return depsGraph;
}

// 递归调用获取依赖
function getDeps(temp,{deps}) {
  Object.keys(deps).forEach(key=>{
    const child = getModuleInfo(deps[key]);
    temp.push(child);
    getDeps(temp,child);
  })
}

// const content = parseModules('./src/index.js');
// console.log(content);


// 打包
function bundle(file) {
  const depsGraph = JSON.stringify(parseModules(file));
  return `(function (graph){
    function require(file){
      function absRequire(relPath){
        return require(graph[file].deps[relPath])
      }
      var exports = {};
      (function (require,exports,code){
        eval(code);
      })(absRequire,exports,graph[file].code)
      return exports
    }
    require('${file}')
  })(${depsGraph})
  `;
}

const content = bundle('./src/index.js');
!fs.existsSync('./dist') && fs.mkdirSync('./dist');
fs.writeFileSync('./dist/bundle.js',content)